//===============================================================================
// Microsoft patterns & practices
// Guidance Explorer Offline Client
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================



using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using GuidanceExplorer.Controls;
using GuidanceExplorer.Model;

namespace GuidanceExplorer.Services
{
    public class WinFormsRenderService : IWinFormsRenderService
    {
        /// <summary>
        /// Render an instance of the Guidance model into a TreeView control.
        /// </summary>
        /// <param name="guidance">The instance in the model.</param>
        /// <param name="treeView">The tree view instance to render to.</param>
        public void RenderGuidanceToTreeView(Guidance guidance, TreeView treeView)
        {
            treeView.Nodes.Clear();

            //Create root nodes: What's New, My Views, and Libraries.
            MyViewsNode myViewsNode = MyViewsNode.New();
            LibrariesNode librariesNode = LibrariesNode.New();
            SearchViewNode searchViewNode = null;

            if (guidance.ClientFlavour == ClientFlavour.Offline)
            {
                //Set the model instance to the my views node.
                myViewsNode.ModelInstance = guidance.UserViews;

                //Add nodes to the TreeView.
                treeView.Nodes.Add(myViewsNode);
                treeView.Nodes.Add(librariesNode);

                //Populate My Views node.
                RenderFolderStructureToTreeNode(treeView, guidance.UserViews, myViewsNode, guidance);

                //Populate Libraries node.
                RenderLibraryCollectionToLibrariesNode(treeView, guidance, guidance.Libraries, librariesNode);

                if (treeView.Nodes[0].Text != Messages.WHATS_NEW_VIEW)
                {
                    SearchView searchView = SearchView.New();
                    searchView.Author = "guidancelibrary";
                    searchView.Description = "";
                    searchView.ContainerScope = guidance;
                    searchView.RootView = true;
                    searchView.Caption = Messages.WHATS_NEW_VIEW;
                    searchView.LastUpdate = DateTime.UtcNow;
                    searchView.Criteria = new TopSearchCriteria(new List<Criteria>(), 1000);

                    //Populate the cols.
                    searchView.Columns = ColumnCollection.DefaultColumns(GuidanceType.Get(GuidanceType.DefaultTypeName));
                    foreach (Column col in searchView.Columns.Values)
                    {
                        if (string.Compare(col.Field.Caption, Messages.DATE_FIELD) == 0)
                        {
                            col.Sort = SortOrder.Descending;
                        }
                        else
                        {
                            col.Sort = SortOrder.None;
                        }
                    }

                    searchViewNode = SearchViewNode.New();
                    searchViewNode.Text = searchView.Caption;
                    searchViewNode.IsEditable = false;
                    searchViewNode.ModelInstance = searchView;
                    searchView.ContainerScope = guidance;

                    treeView.Nodes.Insert(0, searchViewNode);
                }
                else
                {
                    searchViewNode = treeView.Nodes[0] as SearchViewNode;
                    SearchView searchView = searchViewNode.ModelInstance;

                    if (guidance.UserViews.Views.ContainsKey(searchView.Caption))
                    {
                        guidance.UserViews.Views.Remove(searchView.Caption);
                    }

                    searchViewNode.IsEditable = false;

                    if (searchViewNode.ModelInstance.Columns == null || searchViewNode.ModelInstance.Columns.Count == 0)
                    {
                        searchViewNode.ModelInstance.Columns = ColumnCollection.DefaultColumns(GuidanceType.New(GuidanceType.DefaultTypeName));

                        foreach (Column col in searchViewNode.ModelInstance.Columns.Values)
                        {
                            if (string.Compare(col.Field.Caption, Messages.DATE_FIELD) == 0)
                            {
                                col.Sort = SortOrder.Descending;
                            }
                            else
                            {
                                col.Sort = SortOrder.None;
                            }
                        }
                    }
                }
                RenderItemsToViewNode(guidance, searchViewNode.ModelInstance, searchViewNode);
                //Expand the libraries node 
                librariesNode.Expand();
                foreach (TreeNode libraryNode in librariesNode.Nodes)
                {
                    libraryNode.Expand();
                }

                //expand myviews node
                //myViewsNode.Expand();

                if (searchViewNode.Text == Messages.WHATS_NEW_VIEW)
                {
                    //Set the what's new view node.
                    treeView.SelectedNode = searchViewNode;
                }
                else
                {
                    //Set the libraries node.
                    treeView.SelectedNode = librariesNode;
                }
            }
            else if (guidance.ClientFlavour == ClientFlavour.AuthoringToolkit)
            {
                AuthoringToolkitNode authoringToolkitNode = AuthoringToolkitNode.New();

                //Add nodes to the TreeView.
                treeView.Nodes.Add(authoringToolkitNode);

                //Set the libraries node.
                treeView.SelectedNode = authoringToolkitNode;
            }

            //Set the focus to the treeView so the selection is visible.
            treeView.Select();

        }


        /// <summary>
        /// Renders the UserViews in the MyViews node in the TreeView
        /// </summary>
        /// <param name="views">The user views and folders in the model.</param>
        /// <param name="myViewsNode">The node where the user views must be placed.</param>
        protected void RenderFolderStructureToTreeNode(TreeView treeView, FolderStructure folderStructure, TreeNode treeNode, Guidance guidance)
        {
            //Iterate through all the folders.
            foreach (Folder folder in folderStructure.Folders.Values)
            {
                //Create a new folder node.
                FolderNode folderNode = FolderNode.New();

                //Add the node to the my views node.
                treeNode.Nodes.Add(folderNode);

                //Render the Folder to the FolderNode
                RenderFolderToFolderNode(treeView, folder, folderNode, guidance);
            }

            //Iterate through all the views.
            foreach (GuidanceExplorer.Model.View view in folderStructure.Views.Values)
            {
                SearchView searchView = view as SearchView;
                if (searchView != null)
                {
                    //Create the search view node.
                    SearchViewNode searchViewNode = SearchViewNode.New();
                    searchViewNode.Text = searchView.Caption;
                    searchViewNode.ModelInstance = searchView;
                    searchView.ContainerScope = guidance;
                   // RenderItemsToViewNode(guidance, searchView, searchViewNode);
                    if (view.RootView)
                    {
                        treeView.Nodes.Insert(0, searchViewNode);
                    }
                    else
                    {
                        treeNode.Nodes.Add(searchViewNode);
                    }
                }
                else
                {
                    //Create a new view node.
                    ViewNode viewNode = ViewNode.New();

                    //Set the node properties
                    viewNode.Text = view.Caption;
                    viewNode.ModelInstance = view;
                    RenderItemsToViewNode(guidance, view, viewNode);

                    //Add the node to the my views node.
                    if (view.RootView)
                    {
                        treeView.Nodes.Insert(0, viewNode);
                    }
                    else
                    {
                        treeNode.Nodes.Add(viewNode);
                    }
                }
            }
            foreach (Checklist checklist in folderStructure.Checklists.Values)
            {
                //Create a new search view node.
                ChecklistNode checklistNode = ChecklistNode.New();

                //Set the node properties
                checklistNode.Text = checklist.Caption;
                checklistNode.ModelInstance = checklist;

                RenderItemsToViewNode(guidance, checklist, checklistNode);

                treeNode.Nodes.Insert(0, checklistNode);


            }
        }

        /// <summary>
        /// Renders a collection of libraries into a LibrariesNode in the TreeView.
        /// </summary>
        /// <param name="libraries"></param>
        /// <param name="librariesNode"></param>
        private void RenderLibraryCollectionToLibrariesNode(TreeView treeView, Guidance guidance, LibraryCollection libraries, LibrariesNode librariesNode)
        {
            try
            {
                //Iterate throug all the libraries.
                foreach (KeyValuePair<string, Library> library in libraries)
                {
                    RenderLibraryToLibrariesNode(treeView, guidance, library.Value, librariesNode);
                }
            }
            catch (Exception ex)
            {
                //catch this to ensure the type nodes are added
                MessageBox.Show("An application execution error has occured: \n\n" + ex.ToString(), "Guidance Explorer", MessageBoxButtons.OK);
            }
        }

        /// <summary>
        /// Renders a library into a LibraryNode in the TreeView.
        /// </summary>
        /// <param name="library"></param>
        /// <param name="libraryNode"></param>
        public void RenderLibraryToLibrariesNode(TreeView treeView, Guidance guidance, Library library, LibrariesNode librariesNode)
        {
            //Create a new library node.
            LibraryNode libraryNode = LibraryNode.New();

            //Add the node to the libraries node.
            librariesNode.Nodes.Add(libraryNode);

            //Render the library to the library node.
            RenderLibraryToLibraryNode(treeView, guidance, library, libraryNode);
        }

        /// <summary>
        /// Renders a Library into a LibraryNode instance.
        /// </summary>
        /// <param name="library">The library instance to render.</param>
        /// <param name="libraryNode">The library node to render to.</param>
        private void RenderLibraryToLibraryNode(TreeView treeView, Guidance guidance, Library library, LibraryNode libraryNode)
        {
            //Set the node properties
            libraryNode.Text = library.Caption;
            libraryNode.ModelInstance = library;

            //Iterate through all the folders.
            foreach (Folder folder in library.Folders.Values)
            {
                //Create a new folder node.
                FolderNode folderNode = FolderNode.New();

                //Add the node to the library node.
                libraryNode.Nodes.Add(folderNode);

                //Render the Folder to the FolderNode
                RenderFolderToFolderNode(treeView, folder, folderNode, guidance);
            }

            //Iterate through all the views.
            foreach (GuidanceExplorer.Model.View view in library.Views.Values)
            {
                SearchView searchView = view as SearchView;
                if (searchView == null)
                {
                    //Create a new view node.
                    ViewNode viewNode = ViewNode.New();

                    //Set the node properties
                    viewNode.Text = view.Caption;
                    viewNode.ModelInstance = view;

                    //Add the node to the my views node.
                    libraryNode.Nodes.Add(viewNode);

                    RenderItemsToViewNode(guidance, view, viewNode);
                }
                else
                {
                    //Create a new search view node.
                    SearchViewNode searchViewNode = SearchViewNode.New();
                    searchView.ContainerScope = guidance;

                    //Set the node properties
                    searchViewNode.Text = searchView.Caption;
                    searchViewNode.ModelInstance = searchView;

                    //Add the node to the my views node.
                    libraryNode.Nodes.Add(searchViewNode);
                }
            }

            //Create all the guidance type nodes.
            foreach (GuidanceType guidanceType in GuidanceType.AllGuidanceTypes)
            {
                //Guidance type with display name as empty, means not visible in the UI.
                if (!String.IsNullOrEmpty(guidanceType.DisplayName))
                {
                    RefreshGuidanceTypeNodes(libraryNode, guidanceType);
                }
            }

        }

        public void RenderItemsToViewNode(Guidance guidance, GuidanceExplorer.Model.View view, TreeNode viewNode)
        {
            System.Diagnostics.Debug.WriteLineIf(view is SearchView,"Populating view " + view.Caption);
            foreach (GuidanceItem item in view.Items)
            {
                GuidanceItemFileReference fileRef = item as GuidanceItemFileReference;
                if (fileRef == null)
                {
                    fileRef = guidance.GetGuidanceItemFileReference(item);
                }
                //If the item is a file reference it means it exists.
                if (fileRef != null)
                {
                    GuidanceItemNode itemNode = GuidanceItemNode.New();
                    itemNode.Text = fileRef.Title;
                    itemNode.ModelInstance = fileRef;
                    
                    viewNode.Nodes.Add(itemNode);
                }
            }
        }

        /// <summary>
        /// Refresh the GuidanceType nodes that belong to the library
        /// </summary>
        /// <param name="libraryNode">LibraryNode to refresh</param>
        /// <param name="guidanceType">GuidanceType updated</param>
        public void RefreshGuidanceTypeNodes(LibraryNode libraryNode, GuidanceType guidanceType)
        {
            int itemsCount = 0;

            if (string.IsNullOrEmpty(guidanceType.NodeDisplayName))
            {
                itemsCount = libraryNode.ModelInstance.GuidanceItemCountInLibrary(guidanceType);
            }
            else
            {
                itemsCount = libraryNode.ModelInstance.GuidanceItemCountInLibrary(guidanceType.NodeDisplayName);
            }

            if (itemsCount > 0)
            {
                if (!libraryNode.HasGuidanceTypeNode(guidanceType))
                {
                    GuidanceTypeNode guidanceTypeNode = GuidanceTypeNode.New();
                    guidanceTypeNode.Text = guidanceType.ListDisplayName;
                    guidanceTypeNode.ModelInstance = guidanceType;
                    libraryNode.AddGuidanceTypeNode(guidanceTypeNode);

                    if (!string.IsNullOrEmpty(guidanceType.ImageFileName))
                    {
                        //Set the icon to the guidance type and guidance type node.
                        string fullImageFileName = Path.GetFullPath(Path.Combine(ModelStorageService.TemplateFolder, guidanceType.ImageFileName));
                        if (File.Exists(fullImageFileName))
                        {
                            using (FileStream fileStream = new FileStream(fullImageFileName, FileMode.Open, FileAccess.Read))
                            {
                                Image nodeImage = Image.FromStream(fileStream);
                                int imageIndex = libraryNode.TreeView.ImageList.Images.Add(nodeImage, Color.Magenta);
                                guidanceTypeNode.ImageIndex = guidanceTypeNode.SelectedImageIndex = imageIndex;
                            }
                        }
                    }
                    RenderItemsToGuidanceTypeNode(libraryNode, guidanceTypeNode);
                    //Expand the LibraryNode
                    if (!libraryNode.IsExpanded)
                    {
                        libraryNode.Expand();
                    }
                }
                else
                {
                    GuidanceTypeNode guidanceTypeNode = libraryNode.GetGuidanceTypeNode(guidanceType);
                    guidanceTypeNode.Nodes.Clear();
                    RenderItemsToGuidanceTypeNode(libraryNode, guidanceTypeNode);
                }
            }
            else if (libraryNode.HasGuidanceTypeNode(guidanceType))
            {
                libraryNode.RemoveGuidanceTypeNode(guidanceType);
            }
        }   

        private void RenderItemsToGuidanceTypeNode(LibraryNode libraryNode,GuidanceTypeNode guidanceTypeNode)
        {
            //Create the list to display
            GuidanceItemFileReferenceCollection guidanceItems = libraryNode.ModelInstance.GetGuidanceItemFileReferenceForGuidanceType(guidanceTypeNode.ModelInstance);
            foreach (GuidanceItemFileReference item in guidanceItems)
            {
                GuidanceItemNode node = GuidanceItemNode.New();
                node.Text = item.Title;
                node.ModelInstance = item;
                guidanceTypeNode.Nodes.Add(node);
            }
        }

        /// <summary>
        /// Renders a Folder into a FolderNode.
        /// </summary>
        /// <param name="folder">The folder to render.</param>
        /// <param name="folderNode">The folder node to render to.</param>
        public void RenderFolderToFolderNode(TreeView treeView, Folder folder, FolderNode folderNode, Guidance guidance)
        {
            //Set the node properties
            folderNode.Text = folder.Caption;
            folderNode.ModelInstance = folder;

            //Render the child folders.
            RenderFolderCollectionToFolderNode(treeView, folder.Folders as FolderCollection, folderNode, guidance);

            //Iterate through all the views.
            foreach (GuidanceExplorer.Model.View view in folder.Views.Values)
            {
                SearchView searchView = view as SearchView;
                if (searchView != null)
                {
                    //Create a new search view node.
                    SearchViewNode searchViewNode = SearchViewNode.New();

                    searchView.ContainerScope = guidance;

                    //Set the node properties
                    searchViewNode.Text = searchView.Caption;
                    searchViewNode.ModelInstance = searchView;

                    //RenderItemsToViewNode(guidance, searchView, searchViewNode);
                    //Add the node to the my views node.
                    if (view.RootView)
                    {
                        treeView.Nodes.Insert(0, searchViewNode);
                    }
                    else
                    {
                        folderNode.Nodes.Add(searchViewNode);
                    }
                }
                else
                {
                    //Create a new view node.
                    ViewNode viewNode = ViewNode.New();

                    //Set the node properties
                    viewNode.Text = view.Caption;
                    viewNode.ModelInstance = view;

                    RenderItemsToViewNode(guidance,view, viewNode);


                    //Add the node to the my views node.
                    if (view.RootView)
                    {
                        treeView.Nodes.Insert(0, viewNode);
                    }
                    else
                    {
                        folderNode.Nodes.Add(viewNode);
                    }
                }
            }
            foreach (Checklist checklist in folder.Checklists.Values)
            {
                //Create a new search view node.
                ChecklistNode checklistNode = ChecklistNode.New();

                //Set the node properties
                checklistNode.Text = checklist.Caption;
                checklistNode.ModelInstance = checklist;

                RenderItemsToViewNode(guidance, checklist, checklistNode);

                folderNode.Nodes.Insert(0, checklistNode);
                
                
            }
        }

        /// <summary>
        /// Renders the Folder into the folderNode.
        /// </summary>
        /// <param name="folders">The list of folders to render.</param>
        /// <param name="parentFolderNode">The parent node where the folder will be created.</param>
        private void RenderFolderCollectionToFolderNode(TreeView treeView, FolderCollection folders, FolderNode parentFolderNode, Guidance guidance)
        {
            //Iterate through all the folders
            foreach (Folder folder in folders.Values)
            {
                //Create the folder node
                FolderNode folderNode = FolderNode.New();

                //Add the folder node to the parent node
                parentFolderNode.Nodes.Add(folderNode);

                //Render folder to the node.
                RenderFolderToFolderNode(treeView, folder, folderNode, guidance);
            }
        }

        /// <summary>
        /// Populates the given ComboBox with the items' information
        /// </summary>
        /// <param name="combo"></param>
        /// <param name="field"></param>
        /// <param name="items"></param>
        public void PopulateCombo(ComboBox combo, Field field, GuidanceItemCollection items)
        {
            List<string> values = new List<string>();

            foreach (GuidanceItem item in items)
            {
                string value;
                if (item.CustomData.ContainsKey(field.Name))
                {
                    value = item[field.Name] ?? string.Empty;
                }
                else
                {
                    value = string.Empty;
                }
                if (!values.Contains(value))
                {
                    values.Add(value);
                }
            }

            values.Sort();

            combo.Items.Clear();
            combo.Items.AddRange(values.ToArray());
        }

        /// <summary>
        /// Gets the Tree Node asociated to the Model Instance
        /// </summary>
        /// <param name="library"></param>
        /// <returns></returns>
        public LibraryNode GetLibraryNodeByLibrary(TreeView treeView, Library library)
        {
            foreach (TreeNode node in treeView.Nodes)
            {
                LibrariesNode libsNode = node as LibrariesNode;
                if (libsNode == null)
                {
                    continue;
                }

                foreach (TreeNode libNode in libsNode.Nodes)
                {
                    IModelItem modelNode = libNode as IModelItem;
                    if (modelNode == null)
                    {
                        continue;
                    }

                    LibraryNode lib = modelNode as LibraryNode;
                    if (lib != null && lib.ModelInstance == library)
                    {
                        return lib;
                    }
                }
            }

            return null;
        }

        public LibrariesNode GetLibrariesNode(TreeView treeView)
        {
            foreach (TreeNode node in treeView.Nodes)
            {
                LibrariesNode libsNode = node as LibrariesNode;
                if (libsNode != null)
                {
                    return libsNode;
                }
            }
            return null;
        }

        public GuidanceItemNode GetGuidanceItemNode(TreeView treeView,GuidanceItemFileReference guidanceItem)
        {
            return GetGuidanceItemNode(treeView, null, guidanceItem);
        }
        
        public GuidanceItemNode GetGuidanceItemNode(TreeView treeView, TreeNode parentNode, GuidanceItemFileReference guidanceItem)
        {
            if (parentNode != null)
            {
                return GetGuidanceItemNode(parentNode, guidanceItem);
            }
            else if (guidanceItem.Library != null)
            {
                //search under library node
                LibraryNode libNode = GetLibraryNodeByLibrary(treeView, guidanceItem.Library);
                if (libNode != null)
                {
                    libNode.Expand();
                    GuidanceTypeNode typeNode = libNode.GetGuidanceTypeNode(guidanceItem.GuidanceType);
                    if (typeNode != null)
                    {
                        typeNode.Expand();
                        foreach (TreeNode node in typeNode.Nodes)
                        {
                            if (node is GuidanceItemNode && ((GuidanceItemNode)node).ModelInstance.Equals(guidanceItem))
                            {
                                return node as GuidanceItemNode;
                            }
                        }
                    }
                }
            }
            else 
            {
                //search under solution node
                SolutionNode solNode = GetSolutionNode(treeView);
                if (solNode != null)
                {
                    foreach (TreeNode node in solNode.Nodes)
                    {
                        GuidanceItemNode itemNode = GetGuidanceItemNode(node, guidanceItem);
                        if (itemNode != null)
                        {
                            return itemNode;
                        }
                    }
                }
            }
            return null;
        }

        private GuidanceItemNode GetGuidanceItemNode(TreeNode node, GuidanceItemFileReference guidanceItem)
        {
            if (node is GuidanceItemNode && ((GuidanceItemNode)node).ModelInstance.Equals(guidanceItem))
            {
                return node as GuidanceItemNode;
            } 
            foreach (TreeNode childNode in node.Nodes)
            {
                GuidanceItemNode itemNode = GetGuidanceItemNode(childNode, guidanceItem);
                if (itemNode != null)
                {
                    return itemNode;
                }
            }
            return null;
        }

        public void RenderSolutionToTreeView(Guidance guidance,
           TreeView treeView)
        {
            if (guidance.SolutionViews != null)
            {
                SolutionNode solutionNode = SolutionNode.New();
                solutionNode.ModelInstance = guidance.SolutionViews;
                solutionNode.Text = guidance.SolutionViews.SolutionName; //Caption;
                treeView.Nodes.Insert(0, solutionNode);
                //RenderFolderToFolderNode(treeView, solutionGuidance.SolutionFolder, solutionNode, solutionGuidance);
                RenderFolderStructureToTreeNode(treeView, guidance.SolutionViews, solutionNode, guidance);
                solutionNode.Expand();
            }
        }

        public void RefreshSolutionToTreeView(Guidance guidance,
           TreeView treeView)
        {
            if (treeView.Nodes[0] is SolutionNode)
            {
                treeView.Nodes.Remove(treeView.Nodes[0]);
                RenderSolutionToTreeView(guidance, treeView);
            }
        }
        /// <summary>
        /// Return the Solution node
        /// </summary>
        public SolutionNode GetSolutionNode(TreeView treeView)
        {
            foreach (TreeNode node in treeView.Nodes)
            {
                if (node is SolutionNode)
                {
                    return node as SolutionNode;
                }
            }
            return null;
        }
    }
}

